package com.asen.commons.core.meta.entity;

import com.asen.commons.annotation.*;
import com.asen.commons.core.base.enums.DecimalTypeEnum;
import com.asen.commons.core.base.domain.RootModel;
import com.asen.commons.core.base.domain.Core;
import com.asen.commons.core.base.enums.JdbcTypeEnum;
import com.asen.commons.core.meta.loader.EntityObjectLoader;
import com.asen.commons.core.utils.EntityUtils;
import com.asen.commons.utils.StringUtils;
import com.asen.commons.exception.TypeException;
import com.asen.commons.core.base.IntegerEnum;
import com.asen.commons.core.base.ObjectEnum;
import com.asen.commons.core.base.StringEnum;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
 * 实体表列
 *
 * @author ls
 * @version 1.0
 * @date 2024-04-09
 */
@Data
public class FieldObject {

    /**
     * 属性
     */
    private Field field;

    /**
     * 类型
     */
    private Class<?> javaType;

    /**
     * 关联属性
     */
    private FieldReference reference;

    /**
     * 关联属性
     */
    private FieldEnum fieldEnum;

    /**
     * 数据字典
     */
    //private FieldDict fieldDict;

    /**
     * 单据类型
     */
    private FieldBillType fieldBillType;

    /**
     * 忽略属性
     */
    private FieldIgnore fieldIgnore;

    /**
     * 属性名
     */
    private String name;

    /**
     * 属性别名
     */
    private String alias;

    /**
     * 关联属性
     */
    private String refKey;

    /**
     * 关联表
     */
    private String refTable;

    /**
     * 字段名
     */
    private String column;

    /**
     * 字段类型
     */
    private String columnType;

    /**
     * (可选) 标识是否为唯一主键字段.
     */
    private boolean unique;

    /**
     * (可选) 标识列是否允许为空
     */
    private boolean nullable;

    /**
     * (可选) 列长度
     * <p>用于字符串或整数类型字段</>
     */
    private int length;

    /**
     * (可选) 精度
     * <p>用于decimal等数字或小数类型</p>
     */
    private int precision;

    /**
     * (可选) 范围（小数位）
     * <p>用于decimal等数字或小数类型</p>
     */
    private int scale;

    /**
     * 描述信息
     */
    private String desc;

    /**
     * 实体类型,全编码
     */
    private String modelType;

    public FieldObject() {
    }

    public FieldObject(EntityField entityField) {
        this(entityField.getField(), entityField.getJavaType());
    }

    public FieldObject(Field field, Class<?> javaType) {
        this.field = field;
        this.javaType = javaType;
        this.name = field.getName();

        if (field.isAnnotationPresent(FieldIgnore.class)) {
            this.fieldIgnore = field.getAnnotation(FieldIgnore.class);
        }

        if (field.isAnnotationPresent(FieldEnum.class)) {
            this.fieldEnum = field.getAnnotation(FieldEnum.class);
        }

        //if (field.isAnnotationPresent(FieldDict.class)) {
        //    this.fieldDict = field.getAnnotation(FieldDict.class);
        //}

        if (field.isAnnotationPresent(FieldReference.class)) {
            this.reference = field.getAnnotation(FieldReference.class);
        }

        if (field.isAnnotationPresent(FieldBillType.class)) {
            this.fieldBillType = field.getAnnotation(FieldBillType.class);
        }

        if (field.isAnnotationPresent(ApiModelProperty.class)) {
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            this.alias = apiModelProperty.value();
        }

//        if (field.isAnnotationPresent(FieldProperty.class)) {
//            FieldProperty fieldColumn = field.getAnnotation(FieldProperty.class);
//            setFieldColumn(fieldColumn);
//        }
    }

    //public void setFieldColumn(FieldProperty fieldColumn) {
    //    if (StringUtils.isNotEmpty(fieldColumn.name())) {
    //        this.name = fieldColumn.name();
    //    }
    //
    //    if (StringUtils.isNotEmpty(fieldColumn.value())) {
    //        this.alias = fieldColumn.value();
    //    }
    //
    //    if (StringUtils.isNotEmpty(fieldColumn.refKey())) {
    //        this.refKey = fieldColumn.refKey();
    //    }
    //
    //    if (StringUtils.isNotEmpty(fieldColumn.refTable())) {
    //        this.refTable = fieldColumn.refTable();
    //    }
    //
    //    if (StringUtils.isNotEmpty(fieldColumn.column())) {
    //        this.column = fieldColumn.column();
    //    }
    //
    //    if (StringUtils.isNotEmpty(fieldColumn.columnType())) {
    //        this.columnType = fieldColumn.name();
    //    }
    //
    //    this.nullable = fieldColumn.nullable();
    //
    //    if (fieldColumn.length() != 0) {
    //        this.length = fieldColumn.length();
    //    }
    //
    //    if (fieldColumn.precision() != 0) {
    //        this.precision = fieldColumn.precision();
    //    }
    //
    //    if (fieldColumn.scale() != 0) {
    //        this.scale = fieldColumn.scale();
    //    }
    //
    //    if (StringUtils.isNotEmpty(fieldColumn.desc())) {
    //        this.desc = fieldColumn.desc();
    //    }
    //}

    public synchronized void applyColumnNameByDefault() {
        this.column = EntityUtils.camelhumpToUnderline(this.name);

        if (EntityUtils.isSimpleType(this.javaType)) {
            return;
        }

        // 数据字典类型
        //if (DictItem.class.isAssignableFrom(this.javaType)) {
        //    return;
        //}

        if (Core.class.isAssignableFrom(this.javaType)) {
            this.column = this.column + "_id";
            return;
        }

        // 暂不支持其它不具有ID属性的复杂数据类型
        EntityObject subEntity = EntityObjectLoader.getInstance().load(javaType);
        if (subEntity != null && subEntity.hasField("id")) {
            this.column = this.column + "_id";
            return;
        }

        //        throw new TypeException("不支持的类型【" + javaType.getName() + "】，属性【" + field.getName() + "】");
    }

    public synchronized void applyColumnTypeByDefault() {
        boolean unique = false;
        boolean nullable = true;

        if (String.class.isAssignableFrom(javaType)) {
            this.columnType = "VARCHAR";
            this.length = 100;
            return;
        }

        if (Integer.class.isAssignableFrom(javaType)
                || javaType.isAssignableFrom(int.class)) {
            this.columnType = JdbcTypeEnum.INTEGER.getDesc();
            this.length = 11;
            return;
        }

        if (Long.class.isAssignableFrom(javaType)
                || long.class.isAssignableFrom(javaType)) {
            this.columnType = JdbcTypeEnum.LONG.getDesc();
            this.length = 20;
            return;
        }

        if (BigDecimal.class.isAssignableFrom(javaType)) {
            this.columnType = JdbcTypeEnum.DECIMAL.getDesc();
            this.precision = 20;
            this.scale = 10;
            return;
        }

        if (Boolean.class.isAssignableFrom(javaType)
                || boolean.class.isAssignableFrom(javaType)) {
            this.columnType = JdbcTypeEnum.BOOLEAN.getDesc();
            this.length = 1;
            return;
        }

        if (Date.class.isAssignableFrom(javaType)
                || java.sql.Date.class.isAssignableFrom(javaType)) {
            this.columnType = JdbcTypeEnum.DATETIME.getDesc();
            return;
        }

        if (StringEnum.class.isAssignableFrom(javaType)) {
            this.columnType = JdbcTypeEnum.STRING_ENUM.getDesc();
            return;
        }

        if (IntegerEnum.class.isAssignableFrom(javaType)) {
            this.columnType = JdbcTypeEnum.INTEGER_ENUM.getDesc();
            return;
        }

        //if (DictItem.class.isAssignableFrom(javaType)) {
        //    this.columnType = JdbcTypeEnum.STRING_ENUM.getDesc();
        //    return;
        //}

        // 暂不支持不在范围内的其它简单数据类型
        if (EntityUtils.isSimpleType(this.javaType)) {
            throw new TypeException("不支持的类型【" + javaType.getName() + "】，属性【" + field.getName() + "】");
        }

        if (Core.class.isAssignableFrom(javaType)) {
            this.columnType = JdbcTypeEnum.LONG.getDesc();
            return;
        }

        // 暂不支持其它不具有ID属性的复杂数据类型
        EntityObject subEntity = EntityObjectLoader.getInstance().get(javaType.getClass());
        if (subEntity != null && subEntity.hasField("id")) {
            this.columnType = JdbcTypeEnum.LONG.getDesc();
            return;
        }

        //        throw new TypeException("不支持的类型【" + javaType.getName() + "】，属性【" + field.getName() + "】");
    }

    public Object getValue(Object model) throws IllegalAccessException {
        if (model == null) {
            return null;
        }

        field.setAccessible(true);
        return field.get(model);
    }

    public Object getValueJson(Object model) throws IllegalAccessException {
        if (model == null) {
            return null;
        }
        // 优先调用get方法
        try {
            Method method = model.getClass().getMethod("get" + StringUtils.firstAlpha2UpperCase(field.getName()));
            return method.invoke(model);
        } catch (Exception e) {
        }

        field.setAccessible(true);
        return field.get(model);
    }

    public void setValue(Object model, Object value) throws Exception {
        field.setAccessible(true);
        field.set(model, value);
    }

    private Type[] actualParameterTypeArgs;

    public Type[] getActualParameterTypeArgs() {
        try {

            if (actualParameterTypeArgs != null) {
                return actualParameterTypeArgs;
            }

            if (field.getGenericType() == null) {
                return null;
            }

            if (field.getGenericType() instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                if (parameterizedType != null) {
                    actualParameterTypeArgs = (Type[]) parameterizedType.getActualTypeArguments();
                    return actualParameterTypeArgs;
                }
            }

        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }

        return null;
    }

    public Field getField() {
        return field;
    }

    public void setField(Field field) {
        this.field = field;
    }

    public Class<?> getJavaType() {
        return javaType;
    }

    public void setJavaType(Class<?> javaType) {
        this.javaType = javaType;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public String getRefKey() {
        if (StringUtils.isNotEmpty(refKey)) {
            return refKey;
        }

        if (Core.class.isAssignableFrom(this.javaType)) {
            return "id";
        }

        EntityObject eo = EntityObjectLoader.getInstance().load(this.javaType);
        if (eo.hasField("id")) {
            return "id";
        }

        return null;
    }

    public String getRefKeyInner() {
        return refKey;
    }

    public void setRefKey(String refKey) {
        this.refKey = refKey;
    }

    public String getRefTable() {
        return refTable;
    }

    public void setRefTable(String refTable) {
        this.refTable = refTable;
    }

    public String getColumn() {
        if (StringUtils.isEmpty(column)) {
            applyColumnNameByDefault();
        }
        return column;
    }

    public String getColumnInner() {
        return column;
    }

    public String getLeftKeyColumn() {
        return this.getColumn();
    }

    public void setColumn(String column) {
        this.column = column;
    }

    public String getColumnType() {
        if (StringUtils.isEmpty(columnType)) {
            applyColumnTypeByDefault();
        }
        return columnType;
    }

    public String getColumnTypeInner() {
        return columnType;
    }

    public boolean isReference() {
        return reference == null ? false : true;
    }

    public Class<?> getRefType() {
        return reference == null ? null : reference.value();
    }

    public String getRefModelType() {
        return reference == null ? null : reference.value().getName();
    }

    public String getRefAttrs() {
        return reference == null ? null : reference.attrs();
    }

    public boolean isTypeOnly() {
        if (reference != null) {
            return reference.typeOnly();
        }

        if (fieldEnum != null) {
            return fieldEnum.typeOnly();
        }

        //if (fieldDict != null) {
        //    return fieldDict.typeOnly();
        //}

        if (fieldBillType != null) {
            return fieldBillType.typeOnly();
        }

        return false;
    }

    public boolean isEnum() {
        return fieldEnum == null ? false : true;
    }

    public Class<? extends ObjectEnum> getEnumType() {
        return fieldEnum == null ? null : fieldEnum.value();
    }

    public String getEnumDesc() {
        return fieldEnum == null ? null : fieldEnum.attr();
    }

    //public boolean isDict() {
    //    return fieldDict == null ? false : true;
    //}

    public boolean isBillType() {
        return fieldBillType == null ? false : true;
    }

    public String getBillTypeAttr() {
        return fieldBillType == null ? null : fieldBillType.attr();
    }

    //public DictTypeEnum getDictType() {
    //    return fieldDict == null ? null : fieldDict.value();
    //}

    //public String getDictLabel() {
    //    return fieldDict == null ? null : fieldDict.attr();
    //}

    public boolean isIgnore() {
        return fieldIgnore == null ? false : true;
    }

    public boolean isSelectOnly() {
        if (fieldIgnore == null) {
            return false;
        }
        return fieldIgnore.selectOnly();
    }

    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return field.isAnnotationPresent(annotationClass);
    }

    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return field.getAnnotation(annotationClass);
    }

    public boolean isExtFiled() {
        return RootModel.EXTRA_VAR_NAME.equals(name);
    }

    public boolean isCollection() {
        if (EntityUtils.isSimpleType(javaType)) {
            return false;
        }
        if (Map.class.isAssignableFrom(javaType) || Collection.class.isAssignableFrom(javaType)) {
            return true;
        }
        return false;
    }

    public boolean isLCModel() {
        return false;
    }

    public boolean isSubEntry() {
        return this.isSub() || this.isEntry();
    }

    public boolean isSub() {
        if (EntityUtils.isSimpleType(javaType)) {
            return false;
        }
        if (Core.class.isAssignableFrom(javaType)) {
            return true;
        }
        return false;
    }

    public boolean isEntry() {
        if (EntityUtils.isSimpleType(javaType)) {
            return false;
        }
        if (Collection.class.isAssignableFrom(javaType)) {
            return true;
        }
        return false;
    }

    //public boolean isRemoteReference() {
    //    return this.isAnnotationPresent(RemoteReference.class);
    //}

    public boolean isSimpleType() {
        return EntityUtils.isSimpleType(javaType);
    }

    //public boolean isDictItem() {
    //    return DictItem.class.isAssignableFrom(javaType);
    //}

//    public boolean isFieldSubordinate() {
//        return this.isAnnotationPresent(FieldSubordinate.class);
//    }

    public boolean isFieldTypeHandler() {
        return this.isAnnotationPresent(FieldTypeHandler.class);
    }

    public boolean isFieldBool() {
        return this.isAnnotationPresent(FieldBool.class);
    }

    public boolean isFieldDecimal() {
        return this.isAnnotationPresent(FieldDecimal.class);
    }

    public DecimalTypeEnum getDecimalTypeEnum() {
        if (!isFieldDecimal()) {
            return null;
        }
        return getAnnotation(FieldDecimal.class).value();
    }

    public int getDecimalScale() {
        if (!isFieldDecimal()) {
            return -1;
        }
        return getAnnotation(FieldDecimal.class).scale();
    }

    public boolean stripTrailingZeros() {
        if (!isFieldDecimal()) {
            return true;
        }
        return getAnnotation(FieldDecimal.class).stripTrailingZeros();
    }

    public Object newInstance() throws IllegalAccessException, InstantiationException {
        return javaType.newInstance();
    }

    public boolean isDateFormat() {
        return isAnnotationPresent(JsonFormat.class);
    }

    public String getDateFormat() {
        JsonFormat jsonFormat = getAnnotation(JsonFormat.class);
        return jsonFormat.pattern();
    }

    public boolean isJsonIgnore() {
        return isAnnotationPresent(JsonIgnore.class);
    }
}
